package ai.people.platform.archives.service.impl;

import ai.people.core.security.entity.AuthenticationUser;
import ai.people.core.security.support.RequestHolderUtil;
import ai.people.netmon.framework.businessenum.WorkOrderMsgEnum;
import ai.people.netmon.framework.businessenum.WorkOrderRoleEnum;
import ai.people.netmon.framework.businessenum.WorkOrderStateEnum;
import ai.people.netmon.framework.constant.WorkOrderConstant;
import ai.people.netmon.framework.domain.archives.entity.WorkOrder;
import ai.people.netmon.framework.domain.archives.entity.WorkOrderReplay;
import ai.people.netmon.framework.domain.archives.entity.WorkOrderState;
import ai.people.netmon.framework.domain.archives.request.WorkOrderListRequest;
import ai.people.netmon.framework.domain.archives.request.WorkOrderReplayRequest;
import ai.people.netmon.framework.domain.archives.request.WorkOrderRequest;
import ai.people.netmon.framework.domain.archives.vo.WorkOrderDetailVO;
import ai.people.netmon.framework.domain.archives.vo.WorkOrderListVO;
import ai.people.netmon.framework.exception.enums.CommonEnum;
import ai.people.netmon.framework.exception.enums.ExceptionEnum;
import ai.people.netmon.framework.exception.enums.WorkOrderExceptionEnum;
import ai.people.netmon.framework.exception.type.BusinessException;
import ai.people.netmon.framework.model.response.PageResponse;
import ai.people.netmon.framework.utils.AssertUtils;
import ai.people.platform.archives.mapper.WorkOrderMapper;
import ai.people.platform.archives.service.WorkOrderReplayService;
import ai.people.platform.archives.service.WorkOrderService;
import ai.people.platform.archives.service.WorkOrderStateService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static java.util.stream.Collectors.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhenglin
 * @since 2022-05-31
 */
@Service
public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements WorkOrderService {

    @Autowired
    WorkOrderStateService workOrderStateService;
    @Autowired
    WorkOrderMapper workOrderMapper;
    @Autowired
    WorkOrderReplayService workOrderReplayService;

    /**
     * 工单列表(用户)
     *
     * @param request 请求
     * @return {@link List}<{@link WorkOrderListVO}>
     */
    @Override
    public PageResponse<WorkOrderListVO> myWorkOrderList(WorkOrderListRequest request) {
        AuthenticationUser loginUser = RequestHolderUtil.loginUser();
        Long userId = loginUser.getUserId();

        Page<WorkOrder> page = new Page<>(request.getOriginPageNo(), request.getPageSize());
        Page<WorkOrder> result = workOrderMapper.pageCustomUser(page, request, userId);
        return getWorkOrderListVOPageResponse(userId, result);
    }

    private PageResponse<WorkOrderListVO> getWorkOrderListVOPageResponse(Long userId, Page<WorkOrder> result) {
        List<WorkOrder> records = result.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return PageResponse.builderEmpty();
        }

        boolean admin = isWorkOrderAdmin();

        //把上一步骤查出的工单ID聚合出来
        List<Long> ids = records.stream().map(WorkOrder::getId).collect(toList());
        //用ID查出关联的工单状态
        List<WorkOrderState> workOrderStates = workOrderStateService.list(new LambdaQueryWrapper<WorkOrderState>()
                .select(WorkOrderState::getWorkOrderId, WorkOrderState::getOrderType, WorkOrderState::getRoleType, WorkOrderState::getOperatorName)
                .in(WorkOrderState::getWorkOrderId, ids));
        //按工单ID聚合，并提取其中的工单状态
        Map<Long, List<Integer>> orderTypeMap = workOrderStates.stream().collect(groupingBy(WorkOrderState::getWorkOrderId, collectingAndThen(toList(), x -> x.stream().map(WorkOrderState::getOrderType).collect(toList()))));
        //按工单ID聚合，并提取其中管理员的第一个工单状态
        Map<Long, WorkOrderState> stateMap = workOrderStates.stream().collect(groupingBy(WorkOrderState::getWorkOrderId,
                collectingAndThen(toList(), x -> Optional.of(x.stream().filter(y -> Objects.equals(y.getRoleType(), WorkOrderRoleEnum.ADMIN.code)).findFirst()).get().orElseGet(() -> null))
        ));

        List<WorkOrderListVO> voList = new ArrayList<>(records.size());
        //遍历工单填充vo
        for (WorkOrder record : records) {
            Long id = record.getId();
            List<Integer> states = orderTypeMap.getOrDefault(id, Collections.emptyList());
            boolean isAccept = false, isStop = false, isComplete = false, isOperator = false;
            String operatorName = null;
            //如果状态里有工单进行中，则代表已接受工单
            if (states.contains(WorkOrderStateEnum.PROCESSING.code)) {
                isAccept = true;
            }
            //工单中有终止，则代表已终止工单
            if (states.contains(WorkOrderStateEnum.TERMINATED.code)) {
                isStop = true;
            }
            //工单中有完成，则代表已完成工单
            if (states.contains(WorkOrderStateEnum.COMPLETED.code)) {
                isComplete = true;
            }
            //如果工单操作人为空、工单的创建者为当前登录的用户、工单的操作人为当前登录的用户，则'是否是操作人'标志为true
            if (record.getOperatorId() == null || Objects.equals(record.getCreatorId(), userId) || Objects.equals(record.getOperatorId(), userId)) {
                isOperator = true;
            }
            WorkOrderState state = stateMap.get(id);
            if (Objects.nonNull(state)) {
                operatorName = state.getOperatorName();
            }
            voList.add(new WorkOrderListVO(record, isAccept, operatorName, isStop, isComplete, isOperator, admin));
        }
        return PageResponse.builder(voList, result.getTotal(), result.getPages(), result.getCurrent());
    }

    /**
     * 工单列表(管理员)
     *
     * @param request 请求
     * @return {@link PageResponse}<{@link WorkOrderListVO}>
     */
    @Override
    public PageResponse<WorkOrderListVO> adminWorkOrderList(WorkOrderListRequest request) {
        AuthenticationUser loginUser = RequestHolderUtil.loginUser();
        Long userId = loginUser.getUserId();
        Page<WorkOrder> page = new Page<>(request.getOriginPageNo(), request.getPageSize());
        Page<WorkOrder> result = workOrderMapper.pageCustom(page, request);
        return getWorkOrderListVOPageResponse(userId, result);
    }


    /**
     * 新增工单
     *
     * @param addVO 添加
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addWorkOrder(WorkOrderRequest addVO) {
        AuthenticationUser authenticationUser = RequestHolderUtil.loginUser();
        Long userId = authenticationUser.getUserId();
        String username = authenticationUser.getUsername();

        WorkOrder workOrder = new WorkOrder();
        workOrder.setDescribe(addVO.getDescribe());
        workOrder.setContact(addVO.getContact());
        workOrder.setAttachments(addVO.getAttachments());
        workOrder.setCreatorId(userId);
        workOrder.setCreatorName(username);
        workOrder.setHasNewMsg(false);
        workOrder.setHasNewMsgAdmin(true);
        //修改工单状态和更新时间
        workOrder.setOrderType(WorkOrderStateEnum.WAIT_ACCEPT.code);
        Date date = new Date();
        workOrder.setUpdateTime(date);

        boolean save = save(workOrder);
        AssertUtils.isTrue(save, CommonEnum.FAIL);

        Long id = workOrder.getId();
        //增加工单状态为待受理
        WorkOrderState workOrderState = new WorkOrderState();
        workOrderState.setWorkOrderId(id);
        workOrderState.setOrderType(WorkOrderStateEnum.WAIT_ACCEPT.code);
        workOrderState.setRoleType(WorkOrderRoleEnum.USER.code);
        workOrderState.setOperatorId(userId);
        workOrderState.setHasSee(true);
        workOrderState.setUpdateTime(date);
        workOrderState.setOperatorName(username);
        boolean saveState = workOrderStateService.save(workOrderState);
        AssertUtils.isTrue(saveState, CommonEnum.FAIL);
    }

    /**
     * 工单详情
     *
     * @param id id
     * @return {@link WorkOrderDetailVO}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public WorkOrderDetailVO getWorkOrderDetail(Long id) {
        WorkOrder workOrder = getById(id);
        AssertUtils.isNotNull(workOrder, CommonEnum.QUERY_NOT_FOUND);
        List<WorkOrderReplay> orderReplays = workOrderReplayService.list(new LambdaQueryWrapper<WorkOrderReplay>().eq(WorkOrderReplay::getWorkOrderId, id));
        List<WorkOrderState> orderStates = workOrderStateService.list(new LambdaQueryWrapper<WorkOrderState>().eq(WorkOrderState::getWorkOrderId, id));

        WorkOrderDetailVO re = new WorkOrderDetailVO();
        re.setDescribe(workOrder.getDescribe());
        re.setContact(workOrder.getContact());
        re.setAttachments(workOrder.getAttachments());


        List<Object> sortList = new ArrayList<>();

        sortList.addAll(orderReplays);
        sortList.addAll(orderStates);
        //排序 工单回复时间和工单状态时间
        List<Object> sorted = sortList.stream().sorted((o1, o2) -> {
            Date d1, d2;
            d1 = getClassDate(o1);
            d2 = getClassDate(o2);
            return d1.compareTo(d2);
        }).collect(toList());

        List<WorkOrderDetailVO.Msg> list = new ArrayList<>();

        for (Object o : sorted) {
            if (o instanceof WorkOrderReplay) {
                WorkOrderReplay workOrderReplay = (WorkOrderReplay) o;
                //用户回复
                if (Objects.equals(workOrderReplay.getReplayRoleType(), WorkOrderRoleEnum.USER.code)) {
                    list.add(new WorkOrderDetailVO.Msg(workOrderReplay, WorkOrderMsgEnum.USER.code));
                }
                if (Objects.equals(workOrderReplay.getReplayRoleType(), WorkOrderRoleEnum.ADMIN.code)) {
                    list.add(new WorkOrderDetailVO.Msg(workOrderReplay, WorkOrderMsgEnum.ADMIN.code));
                }
            } else if (o instanceof WorkOrderState) {
                //系统消息
                WorkOrderState workOrderState = (WorkOrderState) o;
                list.add(new WorkOrderDetailVO.Msg(workOrderState, WorkOrderMsgEnum.SYSTEM.code));
            }
        }
        re.setMsgList(list);

        //工单详情被查看更新消息被查看
        changeHasSee(workOrder);
        return re;
    }

    /**
     * 更新工单状态已被查看
     *
     * @param workOrder 工作订单
     */
    private void changeHasSee(WorkOrder workOrder) {
        Long id = workOrder.getId();
        Long operatorId = workOrder.getOperatorId();

        Long userId = RequestHolderUtil.getUserId();
        boolean isAdmin = isWorkOrderAdmin();

        //管理员 但非操作人
        if (isAdmin && operatorId != null && !Objects.equals(operatorId, userId)) {
            return;
        }
        int roleType = isAdmin ? WorkOrderRoleEnum.ADMIN.code : WorkOrderRoleEnum.USER.code;

        WorkOrder up = new WorkOrder();
        up.setId(id);

        //根据用户ID查询状态中多少没有被查看的内容
        long stateCount = workOrderStateService.count(new LambdaQueryWrapper<WorkOrderState>()
                .eq(WorkOrderState::getWorkOrderId, id)
                .eq(WorkOrderState::getOperatorId, userId)
                .eq(WorkOrderState::getHasSee, 0));

        if (stateCount > 0) {
            //如果有未查看的内容则修改该用户所有的状态为已查看
            WorkOrderState workOrderState = new WorkOrderState();
            workOrderState.setHasSee(true);
            workOrderStateService.update(workOrderState, new LambdaQueryWrapper<WorkOrderState>()
                    .eq(WorkOrderState::getWorkOrderId, id)
                    .eq(WorkOrderState::getOperatorId, userId)
            );
        }

        //根据角色查询状态中多少没有被查看的内容
        long replayCount = workOrderReplayService.count(new LambdaQueryWrapper<WorkOrderReplay>()
                .eq(WorkOrderReplay::getWorkOrderId, id)
                .eq(WorkOrderReplay::getReplayRoleType, roleType)
                .eq(WorkOrderReplay::getHasSee, 0)
        );
        if (replayCount > 0) {
            //如果有未查看的内容则修改改角色所有的状态为已查看
            WorkOrderReplay workOrderReplay = new WorkOrderReplay();
            workOrderReplay.setHasSee(true);
            workOrderReplayService.update(workOrderReplay, new LambdaQueryWrapper<WorkOrderReplay>()
                    .eq(WorkOrderReplay::getWorkOrderId, id)
                    .eq(WorkOrderReplay::getReplayRoleType, roleType)
            );
        }

        if (isAdmin) {
            up.setHasNewMsgAdmin(false);
        } else {
            up.setHasNewMsg(false);
        }
        boolean b = updateById(up);
        AssertUtils.isTrue(b, CommonEnum.FAIL);
    }


    private Date getClassDate(Object o) {
        if (o instanceof WorkOrderReplay) {
            return ((WorkOrderReplay) o).getReplayTime();
        } else if (o instanceof WorkOrderState) {
            return ((WorkOrderState) o).getUpdateTime();
        }
        return null;
    }

    /**
     * 回复工单
     *
     * @param replayRequest 重复请求
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void replay(WorkOrderReplayRequest replayRequest) {
        Long workOrderId = replayRequest.getId();
        checkOrderIsStop(workOrderId);
        Long userId = RequestHolderUtil.getUserId();
        boolean isAdmin = isWorkOrderAdmin();

        if (isAdmin) {
            //检查是否受理了工单
            long countAccept = workOrderStateService.count(new LambdaQueryWrapper<WorkOrderState>()
                    .eq(WorkOrderState::getWorkOrderId, workOrderId)
                    .eq(WorkOrderState::getOrderType, WorkOrderStateEnum.PROCESSING.code));
            if (countAccept > 0) {
                //是管理员，但是不是受理的那个人
                long count = workOrderStateService.count(new LambdaQueryWrapper<WorkOrderState>()
                        .eq(WorkOrderState::getWorkOrderId, workOrderId)
                        .eq(WorkOrderState::getOperatorId, userId)
                );
                if (count == 0) {
                    throw new BusinessException(WorkOrderExceptionEnum.WORK_ORDER_NOT_ACCEPTOR);
                }
            }
        }

        WorkOrderReplay replay = new WorkOrderReplay();
        replay.setReplayMsg(replayRequest.getReplayMsg());
        replay.setWorkOrderId(workOrderId);
        replay.setReplayAttachments(replayRequest.getReplayAttachments());
        replay.setReplayTime(new Date());
        replay.setReplayRoleType(isAdmin ? WorkOrderRoleEnum.ADMIN.code : WorkOrderRoleEnum.USER.code);
        replay.setOperatorId(userId);

        boolean save = workOrderReplayService.save(replay);
        AssertUtils.isTrue(save, CommonEnum.FAIL);

        //更新工单状态
        WorkOrder workOrder = new WorkOrder();
        workOrder.setId(workOrderId);
        workOrder.setUpdateTime(new Date());
        if (isAdmin) {
            workOrder.setOperatorId(userId);
            workOrder.setHasNewMsg(true);
        } else {
            workOrder.setHasNewMsgAdmin(true);
        }
        boolean b = updateById(workOrder);
        AssertUtils.isTrue(b, CommonEnum.FAIL);
    }

    private boolean isWorkOrderAdmin() {
        return RequestHolderUtil.hasPermission(WorkOrderConstant.WORK_ORDER_MANAGE);
    }


    /**
     * 检查工单是否已完成或者已终止
     *
     * @param id id
     */
    private void checkOrderIsStop(Long id) {
        checkOrder(id, WorkOrderExceptionEnum.WORK_ORDER_IS_STOP, WorkOrderStateEnum.TERMINATED.code, WorkOrderStateEnum.COMPLETED.code);
    }

    private void checkOrder(Long id, ExceptionEnum exceptionEnum, Object... values) {
        long count = workOrderStateService.count(new LambdaQueryWrapper<WorkOrderState>()
                .eq(WorkOrderState::getWorkOrderId, id)
                .in(WorkOrderState::getOrderType, values));
        if (count > 0) {
            throw new BusinessException(exceptionEnum);
        }
    }

    /**
     * 检查工单是否已受理
     *
     * @param id id
     */
    private void checkOrderIsAccept(Long id) {
        checkOrder(id, WorkOrderExceptionEnum.WORK_ORDER_IS_ACCEPT, WorkOrderStateEnum.PROCESSING.code);
    }

    /**
     * 终止工单
     *
     * @param id id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stopWorkOrder(Long id) {
        //已受理工单不能终止
        checkOrderIsAccept(id);
        checkOrderIsStop(id);
        changeStateAndWorkOrderType(id, WorkOrderStateEnum.TERMINATED);
    }

    /**
     * 受理工单
     *
     * @param id id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void acceptWorkOrder(Long id) {
        checkOrderIsStop(id);
        checkOrderIsAccept(id);
        changeStateAndWorkOrderType(id, WorkOrderStateEnum.PROCESSING);
    }

    /**
     * 完成工单
     *
     * @param id id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeWorkOrder(Long id) {
        Long userId = RequestHolderUtil.getUserId();

        long count = workOrderStateService.count(new LambdaQueryWrapper<WorkOrderState>()
                .eq(WorkOrderState::getWorkOrderId, id)
                .eq(WorkOrderState::getOperatorId, userId)
        );
        if (count == 0) {
            throw new BusinessException(WorkOrderExceptionEnum.WORK_ORDER_NOT_OPERATOR);
        }
        changeStateAndWorkOrderType(id, WorkOrderStateEnum.COMPLETED);
    }

    private void changeStateAndWorkOrderType(Long id, WorkOrderStateEnum terminated) {
        AuthenticationUser authenticationUser = RequestHolderUtil.loginUser();
        Long userId = authenticationUser.getUserId();
        String username = authenticationUser.getUsername();
        boolean isAdmin = isWorkOrderAdmin();

        //增加一条工单状态
        WorkOrderState state = new WorkOrderState();
        state.setWorkOrderId(id);
        state.setOrderType(terminated.code);
        state.setRoleType(isAdmin ? WorkOrderRoleEnum.ADMIN.code : WorkOrderRoleEnum.USER.code);
        state.setOperatorId(userId);
        state.setHasSee(false);
        state.setUpdateTime(new Date());
        state.setOperatorName(username);
        boolean save = workOrderStateService.save(state);
        AssertUtils.isTrue(save, CommonEnum.FAIL);

        //更新工单状态
        WorkOrder workOrder = new WorkOrder();
        workOrder.setId(id);

        workOrder.setOrderType(terminated.code);
        workOrder.setUpdateTime(new Date());
        if (isAdmin) {
            workOrder.setOperatorId(userId);
            workOrder.setHasNewMsg(true);
        } else {
            workOrder.setHasNewMsgAdmin(true);
        }

        boolean b = updateById(workOrder);
        AssertUtils.isTrue(b, CommonEnum.FAIL);
    }

}
